Popular Searches
Popular Course Categories
Popular Courses

Understanding responsive application design

Understanding responsive application design

Flutter Responsive Design

Understanding Responsive Application Design in Flutter

Responsive application design means creating a Flutter application that automatically adjusts its layout, spacing, navigation, typography, and content presentation according to the available screen space. A responsive application should provide a usable experience on small phones, large phones, tablets, foldable devices, desktop computers, and web browsers.

Flutter distinguishes between responsive design, which focuses on fitting UI into the available space, and adaptive design, which focuses on making the UI usable in that space. In practice, modern Flutter applications generally need both approaches.

1. What Is Responsive Application Design?

Responsive application design is the process of designing an interface that changes its layout according to the available width and height instead of assuming that every device has the same screen size.

For example:

  • A phone may display one card per row.
  • A tablet may display two or three cards per row.
  • A desktop application may display a sidebar together with the main content.
  • A large screen may use a centered content area instead of stretching text across the entire window.

Simple Example

if (width < 600) {
  // Small-screen layout
} else {
  // Large-screen layout
}

The important idea is that the decision should be based on the available application window size rather than simply asking whether the device is a phone or tablet.

2. Why Responsive Design Is Important

A Flutter application can run on many different screen sizes and form factors. A layout designed only for one screen can produce overflow, excessive empty space, unreadable text, or difficult navigation on another screen.

  • Prevents horizontal and vertical overflow.
  • Improves usability on tablets and desktops.
  • Supports resizable application windows.
  • Works better with split-screen and multi-window layouts.
  • Improves support for foldable and large-screen devices.
  • Makes Flutter web and desktop applications more usable.
  • Allows one codebase to support multiple form factors.

3. Responsive vs Adaptive Design

Responsive Design Adaptive Design
Fits the UI into available space. Chooses a suitable UI structure for the available space.
Changes sizes, positions, spacing, and wrapping. May change navigation, information architecture, or interaction patterns.
Example: changing a 4-column grid to 2 columns. Example: changing bottom navigation to a NavigationRail.
Primarily concerned with layout. Concerned with usability and behavior.

4. Responsive Design Should Be Based on Available Space

A common mistake is writing code such as:

if (isTablet) {
  // Tablet UI
}

This approach can fail because a tablet can run an application in a narrow split-screen window, while a desktop application can be resized to a small window.

A better approach is to ask:

  • How much width does my application currently have?
  • How much height is available?
  • Can these widgets fit horizontally?
  • Should this layout use a Row or Column?
  • Should navigation be a bottom bar or a side rail?

5. The Abstract → Measure → Branch Approach

A useful Flutter approach to adaptive applications can be divided into three steps:

  1. Abstract: Separate common data and UI components.
  2. Measure: Determine the available application or widget space.
  3. Branch: Select the appropriate layout according to the available space.

Example

class Destination {
  final String title;
  final IconData icon;

  const Destination({
    required this.title,
    required this.icon,
  });
}

The same destination data can then be used by different navigation widgets depending on the available width.

6. Understanding Logical Pixels

Flutter uses logical pixels for layout measurements. Logical pixels help applications maintain approximately consistent visual sizing across displays with different physical pixel densities.

final width = MediaQuery.sizeOf(context).width;
final height = MediaQuery.sizeOf(context).height;

For responsive layout decisions, logical dimensions are generally more useful than physical screen resolution.

7. Using MediaQuery

MediaQuery provides information about the current application window, including its size and accessibility/display characteristics.

Basic Example

class ResponsivePage extends StatelessWidget {
  const ResponsivePage({super.key});

  @override
  Widget build(BuildContext context) {
    final size = MediaQuery.sizeOf(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Responsive App'),
      ),
      body: Center(
        child: Text(
          'Width: ${size.width.toStringAsFixed(0)}',
        ),
      ),
    );
  }
}

MediaQuery.sizeOf()

When you only need the application window size, MediaQuery.sizeOf(context) is a focused way to obtain it.

final width = MediaQuery.sizeOf(context).width;

Flutter documentation recommends using the size-specific MediaQuery API when only the size is needed rather than depending on the complete MediaQuery data object.

8. Responsive Layout Using MediaQuery

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;

    if (width < 600) {
      return const MobileLayout();
    }

    return const LargeScreenLayout();
  }
}

class MobileLayout extends StatelessWidget {
  const MobileLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Mobile Layout'),
    );
  }
}

class LargeScreenLayout extends StatelessWidget {
  const LargeScreenLayout({super.key});

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Large Screen Layout'),
    );
  }
}

9. Using LayoutBuilder

LayoutBuilder is useful when a widget needs to respond to the space provided by its parent rather than the size of the entire application window.

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 600) {
      return const Text('Small Layout');
    }

    return const Text('Large Layout');
  },
)

MediaQuery vs LayoutBuilder

MediaQuery LayoutBuilder
Measures the application window. Measures constraints supplied by the parent.
Useful for app-level layout decisions. Useful for component-level layout decisions.
Returns a Size. Returns BoxConstraints.
Example: navigation structure. Example: responsive card component.

10. Understanding BoxConstraints

LayoutBuilder provides a BoxConstraints object.

LayoutBuilder(
  builder: (context, constraints) {
    print(constraints.minWidth);
    print(constraints.maxWidth);
    print(constraints.minHeight);
    print(constraints.maxHeight);

    return Container();
  },
)

Important properties include:

  • minWidth
  • maxWidth
  • minHeight
  • maxHeight

11. Responsive Breakpoints

A breakpoint is a width at which your application changes its layout or behavior.

Example Width Possible Layout
Below 600 logical pixels Compact/mobile layout
600–1023 logical pixels Tablet or medium layout
1024+ logical pixels Expanded desktop/large-screen layout

These values are examples for teaching and should not be treated as universal device categories. Breakpoints should be selected according to the layout's actual space requirements.

12. Creating a Responsive Row and Column

LayoutBuilder(
  builder: (context, constraints) {
    final isSmall = constraints.maxWidth < 600;

    if (isSmall) {
      return Column(
        children: const [
          Text('Item 1'),
          Text('Item 2'),
          Text('Item 3'),
        ],
      );
    }

    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
      children: const [
        Text('Item 1'),
        Text('Item 2'),
        Text('Item 3'),
      ],
    );
  },
)

On a narrow screen, the widgets are stacked vertically. On a wider screen, they are displayed horizontally.

13. Using Expanded for Responsive Width

Expanded allows children of a Row or Column to share the available space.

Row(
  children: [
    Expanded(
      child: Container(
        height: 100,
        child: const Center(
          child: Text('Card 1'),
        ),
      ),
    ),
    const SizedBox(width: 12),
    Expanded(
      child: Container(
        height: 100,
        child: const Center(
          child: Text('Card 2'),
        ),
      ),
    ),
  ],
)

Instead of assigning fixed widths such as width: 300, the children can share whatever horizontal space is available.

14. Using Flexible

Flexible is useful when a child should be allowed to occupy available space without necessarily being forced to fill all of it.

Row(
  children: [
    Flexible(
      child: Text(
        'This text can adapt to the available width.',
      ),
    ),
    const Icon(Icons.info),
  ],
)

15. Responsive Cards

LayoutBuilder(
  builder: (context, constraints) {
    final columns = constraints.maxWidth >= 900
        ? 3
        : constraints.maxWidth >= 600
            ? 2
            : 1;

    return GridView.count(
      crossAxisCount: columns,
      crossAxisSpacing: 16,
      mainAxisSpacing: 16,
      children: List.generate(
        6,
        (index) => Card(
          child: Center(
            child: Text('Card ${index + 1}'),
          ),
        ),
      ),
    );
  },
)

16. Responsive GridView

For large-screen layouts, a grid can prevent cards and text from becoming excessively wide.

GridView.builder(
  padding: const EdgeInsets.all(16),
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 300,
    crossAxisSpacing: 16,
    mainAxisSpacing: 16,
    childAspectRatio: 1.2,
  ),
  itemCount: 20,
  itemBuilder: (context, index) {
    return Card(
      child: Center(
        child: Text('Product ${index + 1}'),
      ),
    );
  },
)

SliverGridDelegateWithMaxCrossAxisExtent can be useful when you want item widths to remain within a reasonable maximum rather than hard-coding a number of columns for every device.

17. Responsive Navigation

Navigation is one of the most important areas of adaptive design. A compact screen may use a bottom navigation bar, while a wider screen may provide navigation through a side rail.

class ResponsiveNavigation extends StatelessWidget {
  const ResponsiveNavigation({super.key});

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 600) {
          return Scaffold(
            body: const Center(
              child: Text('Mobile Content'),
            ),
            bottomNavigationBar: NavigationBar(
              destinations: const [
                NavigationDestination(
                  icon: Icon(Icons.home),
                  label: 'Home',
                ),
                NavigationDestination(
                  icon: Icon(Icons.person),
                  label: 'Profile',
                ),
              ],
            ),
          );
        }

        return Scaffold(
          body: Row(
            children: [
              NavigationRail(
                destinations: const [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('Home'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.person),
                    label: Text('Profile'),
                  ),
                ],
                selectedIndex: 0,
                onDestinationSelected: (index) {},
              ),
              const Expanded(
                child: Center(
                  child: Text('Desktop Content'),
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

18. Responsive Sidebar

LayoutBuilder(
  builder: (context, constraints) {
    final isLarge = constraints.maxWidth >= 900;

    return Row(
      children: [
        if (isLarge)
          Container(
            width: 240,
            color: Colors.grey.shade200,
            child: const Center(
              child: Text('Sidebar'),
            ),
          ),
        Expanded(
          child: Container(
            padding: const EdgeInsets.all(20),
            child: const Text('Main Content'),
          ),
        ),
      ],
    );
  },
)

On large screens, the sidebar is visible. On smaller screens, the sidebar can be replaced by a drawer or another compact navigation pattern.

19. Responsive List and Detail Layout

A common adaptive pattern is displaying a list and detail page separately on small screens but side-by-side on larger screens.

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 700) {
      return const ContactListPage();
    }

    return Row(
      children: [
        SizedBox(
          width: 320,
          child: const ContactListPage(),
        ),
        const VerticalDivider(width: 1),
        const Expanded(
          child: ContactDetailsPage(),
        ),
      ],
    );
  },
)

20. Responsive Forms

Forms should not become excessively wide on large screens. A useful technique is to provide a maximum width.

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 500,
    ),
    child: Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        children: [
          TextField(
            decoration: const InputDecoration(
              labelText: 'Name',
            ),
          ),
          const SizedBox(height: 16),
          TextField(
            decoration: const InputDecoration(
              labelText: 'Email',
            ),
          ),
          const SizedBox(height: 16),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton(
              onPressed: () {},
              child: const Text('Submit'),
            ),
          ),
        ],
      ),
    ),
  ),
)

21. ConstrainedBox for Large Screens

ConstrainedBox can prevent content from becoming unnecessarily wide.

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 800,
    ),
    child: const Padding(
      padding: EdgeInsets.all(24),
      child: Text(
        'This content remains readable on large screens.',
      ),
    ),
  ),
)

22. Responsive Padding

LayoutBuilder(
  builder: (context, constraints) {
    final padding = constraints.maxWidth < 600
        ? 16.0
        : 32.0;

    return Padding(
      padding: EdgeInsets.all(padding),
      child: const Text('Responsive content'),
    );
  },
)

23. Responsive Text

Text should remain readable without being forced into a fixed size that causes overflow.

LayoutBuilder(
  builder: (context, constraints) {
    final fontSize = constraints.maxWidth < 600
        ? 24.0
        : 36.0;

    return Text(
      'Responsive Heading',
      style: TextStyle(
        fontSize: fontSize,
        fontWeight: FontWeight.bold,
      ),
    );
  },
)

In production applications, prefer readable typography systems and constraints instead of continuously scaling every piece of text according to screen width.

24. Using Wrap for Responsive Content

Wrap automatically moves children to another line when there is not enough horizontal space.

Wrap(
  spacing: 10,
  runSpacing: 10,
  children: [
    Chip(label: const Text('Flutter')),
    Chip(label: const Text('Dart')),
    Chip(label: const Text('Firebase')),
    Chip(label: const Text('UI Design')),
    Chip(label: const Text('Mobile')),
  ],
)

This is useful for tags, filters, categories, buttons, and other collections that need to flow across multiple lines.

25. Responsive Images

LayoutBuilder(
  builder: (context, constraints) {
    return Image.network(
      'https://example.com/image.jpg',
      width: constraints.maxWidth,
      fit: BoxFit.cover,
    );
  },
)

For real applications, use appropriate image sizes and caching strategies so that large screens do not unnecessarily download oversized assets.

26. SafeArea

SafeArea helps keep important content away from areas occupied by system UI, display cutouts, and rounded display edges.

Scaffold(
  body: SafeArea(
    child: Column(
      children: [
        const Text('Safe Content'),
        Expanded(
          child: ListView(
            children: const [
              ListTile(title: Text('Item 1')),
              ListTile(title: Text('Item 2')),
            ],
          ),
        ),
      ],
    ),
  ),
)

27. Responsive Dashboard Example

class DashboardPage extends StatelessWidget {
  const DashboardPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dashboard'),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final isLarge = constraints.maxWidth >= 900;

          return Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              children: [
                if (isLarge)
                  const Row(
                    children: [
                      Expanded(child: DashboardCard(title: 'Users')),
                      SizedBox(width: 16),
                      Expanded(child: DashboardCard(title: 'Orders')),
                      SizedBox(width: 16),
                      Expanded(child: DashboardCard(title: 'Revenue')),
                    ],
                  )
                else
                  const Column(
                    children: [
                      DashboardCard(title: 'Users'),
                      SizedBox(height: 12),
                      DashboardCard(title: 'Orders'),
                      SizedBox(height: 12),
                      DashboardCard(title: 'Revenue'),
                    ],
                  ),
              ],
            ),
          );
        },
      ),
    );
  }
}

class DashboardCard extends StatelessWidget {
  final String title;

  const DashboardCard({
    super.key,
    required this.title,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Center(
          child: Text(title),
        ),
      ),
    );
  }
}

28. Complete Responsive Home Screen

class ResponsiveHomePage extends StatelessWidget {
  const ResponsiveHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Responsive App'),
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final width = constraints.maxWidth;

          if (width < 600) {
            return const MobileHome();
          }

          if (width < 1000) {
            return const TabletHome();
          }

          return const DesktopHome();
        },
      ),
    );
  }
}

class MobileHome extends StatelessWidget {
  const MobileHome({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: const [
        Text(
          'Mobile Home',
          style: TextStyle(
            fontSize: 28,
            fontWeight: FontWeight.bold,
          ),
        ),
        SizedBox(height: 20),
        Card(
          child: Padding(
            padding: EdgeInsets.all(20),
            child: Text('Mobile content'),
          ),
        ),
      ],
    );
  }
}

class TabletHome extends StatelessWidget {
  const TabletHome({super.key});

  @override
  Widget build(BuildContext context) {
    return GridView.count(
      padding: const EdgeInsets.all(24),
      crossAxisCount: 2,
      crossAxisSpacing: 20,
      mainAxisSpacing: 20,
      children: List.generate(
        6,
        (index) => Card(
          child: Center(
            child: Text('Tablet Card ${index + 1}'),
          ),
        ),
      ),
    );
  }
}

class DesktopHome extends StatelessWidget {
  const DesktopHome({super.key});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Container(
          width: 240,
          color: Colors.grey.shade200,
          child: const Center(
            child: Text('Sidebar'),
          ),
        ),
        const Expanded(
          child: Padding(
            padding: EdgeInsets.all(32),
            child: Center(
              child: Text(
                'Desktop Content',
                style: TextStyle(fontSize: 32),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

29. Avoid Fixed Widths

Hard-coded widths can cause problems on smaller screens.

// Less flexible
Container(
  width: 500,
  child: const Text('Content'),
)

A more flexible approach is:

Expanded(
  child: Container(
    child: const Text('Content'),
  ),
)

Or use a maximum constraint:

ConstrainedBox(
  constraints: const BoxConstraints(
    maxWidth: 700,
  ),
  child: const Text('Content'),
)

30. Avoid Using Device Type for Layout Decisions

Avoid making layout decisions based on assumptions such as “Android phone”, “tablet”, or “desktop”. The same physical device can run an application in different window sizes.

Prefer available-space measurements:

LayoutBuilder(
  builder: (context, constraints) {
    if (constraints.maxWidth < 600) {
      return const CompactLayout();
    }

    return const ExpandedLayout();
  },
)

31. Avoid Using Orientation Alone

Landscape orientation does not automatically mean that a large layout is appropriate. A narrow application window can still be landscape-oriented.

Instead of depending only on orientation:

final width = MediaQuery.sizeOf(context).width;

Use the available width to determine how much content can actually fit.

32. Do Not Stretch Content Across the Entire Large Screen

Using every available horizontal pixel can make text fields, paragraphs, cards, and other components unnecessarily wide.

Center(
  child: ConstrainedBox(
    constraints: const BoxConstraints(
      maxWidth: 1000,
    ),
    child: const Padding(
      padding: EdgeInsets.all(24),
      child: Text('Main application content'),
    ),
  ),
)

33. Responsive Login Screen

class LoginPage extends StatelessWidget {
  const LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Center(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(24),
            child: ConstrainedBox(
              constraints: const BoxConstraints(
                maxWidth: 450,
              ),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Icon(
                    Icons.lock,
                    size: 70,
                  ),
                  const SizedBox(height: 24),
                  const Text(
                    'Login',
                    style: TextStyle(
                      fontSize: 30,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  const SizedBox(height: 24),
                  const TextField(
                    decoration: InputDecoration(
                      labelText: 'Email',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 16),
                  const TextField(
                    obscureText: true,
                    decoration: InputDecoration(
                      labelText: 'Password',
                      border: OutlineInputBorder(),
                    ),
                  ),
                  const SizedBox(height: 20),
                  SizedBox(
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {},
                      child: const Text('Login'),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

34. Supporting Mouse, Keyboard, and Touch

A truly adaptive application should consider more than screen size. Large-screen applications may be used with a mouse, trackpad, or physical keyboard, while mobile applications are primarily touch-oriented.

  • Support keyboard navigation where appropriate.
  • Provide accessible focus behavior.
  • Consider hover interactions for desktop users.
  • Support scrolling with mouse wheels and touch gestures.
  • Use sufficiently large touch targets.
  • Do not make important actions dependent only on hover.

35. Preserving Application State

Responsive applications should preserve important state when their window changes size, orientation changes, or the layout is rebuilt.

ListView(
  key: const PageStorageKey('products-list'),
  children: const [
    ListTile(title: Text('Product 1')),
    ListTile(title: Text('Product 2')),
    ListTile(title: Text('Product 3')),
  ],
)

PageStorageKey can help preserve widget state such as scroll position when a widget is recreated.

36. Break Large Widgets into Smaller Widgets

Large responsive screens can become difficult to maintain if every layout decision exists inside one massive widget.

Instead of:

class HomePage extends StatelessWidget {
  // Hundreds of lines of UI code
}

Prefer smaller components:

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return const Column(
      children: [
        AppHeader(),
        DashboardSection(),
        RecentProducts(),
        AppFooter(),
      ],
    );
  }
}

Smaller widgets are easier to test, reuse, maintain, and adapt to different screen sizes.

37. Reusable Responsive Builder

class ResponsiveBuilder extends StatelessWidget {
  final Widget mobile;
  final Widget tablet;
  final Widget desktop;

  const ResponsiveBuilder({
    super.key,
    required this.mobile,
    required this.tablet,
    required this.desktop,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < 600) {
          return mobile;
        }

        if (constraints.maxWidth < 1000) {
          return tablet;
        }

        return desktop;
      },
    );
  }
}

Using ResponsiveBuilder

ResponsiveBuilder(
  mobile: const MobileHome(),
  tablet: const TabletHome(),
  desktop: const DesktopHome(),
)

38. Responsive Product Listing Example

class ProductGrid extends StatelessWidget {
  final List products;

  const ProductGrid({
    super.key,
    required this.products,
  });

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      padding: const EdgeInsets.all(16),
      gridDelegate:
          const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 280,
        crossAxisSpacing: 16,
        mainAxisSpacing: 16,
        childAspectRatio: 0.8,
      ),
      itemCount: products.length,
      itemBuilder: (context, index) {
        return Card(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Icon(
                Icons.shopping_bag,
                size: 50,
              ),
              const SizedBox(height: 12),
              Text(products[index]),
            ],
          ),
        );
      },
    );
  }
}

39. Responsive Dialog

showDialog(
  context: context,
  builder: (context) {
    return AlertDialog(
      title: const Text('Responsive Dialog'),
      content: ConstrainedBox(
        constraints: const BoxConstraints(
          maxWidth: 500,
        ),
        child: const Text(
          'The dialog content is constrained so it does not become unnecessarily wide.',
        ),
      ),
      actions: [
        TextButton(
          onPressed: () => Navigator.pop(context),
          child: const Text('Close'),
        ),
      ],
    );
  },
);

40. Responsive Application Structure

A well-organized Flutter project can separate reusable UI components from screen-specific responsive decisions.

lib/
├── main.dart
├── models/
│   ├── product.dart
│   └── user.dart
├── screens/
│   ├── home_page.dart
│   ├── login_page.dart
│   └── dashboard_page.dart
├── widgets/
│   ├── responsive_builder.dart
│   ├── dashboard_card.dart
│   ├── product_card.dart
│   └── app_navigation.dart
└── services/
    └── api_service.dart

41. Responsive Design Testing

Responsive applications should be tested at multiple window sizes rather than only on one physical phone.

  • Test small portrait layouts.
  • Test small landscape layouts.
  • Test tablet-sized windows.
  • Test desktop windows.
  • Resize desktop/web windows manually.
  • Test long text.
  • Test large accessibility text settings.
  • Test keyboard navigation.
  • Test mouse and touch interactions.
  • Test scrolling behavior.
  • Test dialogs and forms at narrow widths.
  • Test large-screen layouts for excessive empty space.

42. Common Responsive Design Mistakes

  • Using fixed widths everywhere.
  • Assuming every phone is the same size.
  • Checking whether the device is a phone or tablet for layout decisions.
  • Using orientation alone to choose a complete layout.
  • Forcing content to use the entire width of a desktop screen.
  • Putting too many widgets inside one large build method.
  • Ignoring keyboard and mouse interaction.
  • Ignoring accessibility text scaling.
  • Not testing resizable windows.
  • Creating different data models for every screen size unnecessarily.
  • Using excessive nested Rows and Columns without constraints.
  • Ignoring SafeArea where system UI can cover important content.

43. Responsive Design Best Practices

  1. Design around available space rather than device names.
  2. Use MediaQuery.sizeOf for application-window measurements.
  3. Use LayoutBuilder for local widget constraints.
  4. Create breakpoints based on actual layout requirements.
  5. Use Expanded, Flexible, and Wrap when appropriate.
  6. Use grids for large collections of content.
  7. Use maximum content widths on large screens.
  8. Keep reusable widgets small and focused.
  9. Preserve important application state.
  10. Support touch, mouse, keyboard, and accessibility needs.
  11. Avoid unnecessary orientation locks.
  12. Test across many window sizes and input methods.

44. Practical Workflow for Building a Responsive Flutter App

  1. Step 1: Identify the important UI components.
  2. Step 2: Separate reusable data from presentation.
  3. Step 3: Design the compact layout.
  4. Step 4: Identify where the compact layout stops working.
  5. Step 5: Define meaningful breakpoints.
  6. Step 6: Use MediaQuery or LayoutBuilder to measure available space.
  7. Step 7: Create alternative layouts where necessary.
  8. Step 8: Add maximum widths to large-screen content.
  9. Step 9: Test resizing, orientation, and different input devices.
  10. Step 10: Refactor repeated responsive logic into reusable widgets.

45. Mini Project: Responsive Flutter Dashboard

Create a dashboard containing:

  • AppBar
  • Navigation
  • Statistics cards
  • Product grid
  • Recent orders list
  • Search field
  • Profile section

Expected behavior:

  • Small width: Use compact navigation and one-column content.
  • Medium width: Use two-column cards and compact navigation.
  • Large width: Display a sidebar and multi-column content.
  • Very large width: Constrain the main content instead of stretching everything across the window.

46. Practice Exercises

  1. Create a responsive login screen that works on phone and desktop.
  2. Create a product grid that automatically changes item width.
  3. Create a dashboard with one, two, and three-column layouts.
  4. Create a responsive navigation system using NavigationBar and NavigationRail.
  5. Create a list-detail application that displays both sections side-by-side on large screens.
  6. Create a responsive registration form with a maximum content width.
  7. Create a responsive profile page with an image, details, and action buttons.
  8. Resize the application window and identify where your layout needs to branch.

47. Quick Revision

Concept Purpose
Responsive Design Fits UI into available space.
Adaptive Design Changes UI structure or behavior to remain usable.
MediaQuery.sizeOf Gets the application window size.
LayoutBuilder Gets local parent constraints.
BoxConstraints Defines minimum and maximum available dimensions.
Expanded Shares available Row/Column space.
Flexible Allows flexible sizing within Row/Column.
Wrap Moves children to additional lines when necessary.
GridView Creates two-dimensional responsive layouts.
ConstrainedBox Limits content to useful dimensions.
SafeArea Protects content from system UI and display cutouts.
PageStorageKey Helps preserve widget state such as scroll position.
Breakpoints Define when layout behavior changes.

48. Key Takeaways

  • Responsive application design allows Flutter UIs to work across different available window sizes.
  • Responsive design focuses on fitting UI into available space.
  • Adaptive design focuses on making the UI usable in that space.
  • Use available window size instead of device type for layout decisions.
  • Use MediaQuery.sizeOf when you need application-window dimensions.
  • Use LayoutBuilder when a component needs to respond to its parent's constraints.
  • Use meaningful breakpoints based on the requirements of the layout.
  • Use flexible widgets instead of unnecessary fixed dimensions.
  • Large-screen layouts should avoid unnecessarily wide text and controls.
  • Responsive applications should consider touch, mouse, keyboard, accessibility, and platform conventions.
  • Always test applications at multiple window sizes.

49. Official Flutter Resources

50. Learn Flutter with JustAcademy

For structured Flutter learning and practical training, explore the JustAcademy Flutter Training Course.

You can also register for a course demonstration through the JustAcademy Flutter Course Demo Registration page.


Conclusion: Understanding responsive application design is essential for building modern Flutter applications. Instead of designing a screen for one specific device, design the application around the space, capabilities, and input methods currently available. By combining MediaQuery, LayoutBuilder, flexible layouts, meaningful breakpoints, constrained content, responsive navigation, and proper testing, you can build Flutter applications that remain usable across phones, tablets, foldables, desktop computers, and web browsers.

Previous Next
whatsapp